#include "lib.h"

/******************************************************************************
 *
 * Définition de la classe Sort
 *
 *****************************************************************************/

Sort::Sort(const unsigned int& ecole, const unsigned int& niveau,
	   const unsigned int& numero)
{
  Ecole  = ecole;
  Niveau = niveau;
  Numero = numero;
}

Sort::Sort(const Sort& m_sort)
{
  Ecole  = m_sort.Ecole;
  Niveau = m_sort.Niveau;
  Numero = m_sort.Numero;  
}

Sort::~Sort()
{}

bool Sort::operator<(const Sort& m_sort) const
{
  if (Ecole < m_sort.Ecole)
    return true;
  else if (Ecole == m_sort.Ecole && Niveau < m_sort.Niveau)
    return true;
  else if (Ecole == m_sort.Ecole && Niveau == m_sort.Niveau && Numero < m_sort.Numero)
    return true;
  else
    return false;
}

bool Sort::operator>(const Sort& m_sort) const
{
  if (Ecole > m_sort.Ecole)
    return true;
  else if (Ecole == m_sort.Ecole && Niveau > m_sort.Niveau)
    return true;
  else if (Ecole == m_sort.Ecole && Niveau == m_sort.Niveau && Numero > m_sort.Numero)
    return true;
  else
    return false;
}

bool Sort::operator==(const Sort& m_sort) const
{
  return ((Ecole == m_sort.Ecole) && (Niveau == m_sort.Niveau) && (Numero == m_sort.Numero));
}

unsigned int Sort::get_Ecole() const
{
  return Ecole;
}

unsigned int Sort::get_Niveau() const
{
return Niveau;
}

unsigned int Sort::get_Numero() const
{
  return Numero;
}

ostream& operator<<(ostream& out, const Sort& m_sort)
{
  return out << setw(2) << m_sort.Ecole << ' ' << m_sort.Niveau << ' ' << setw(2)
	     << m_sort.Numero << ' ';
}

/******************************************************************************
 *
 * Définition des fonctions
 *
 *****************************************************************************/
  
/** @addtogroup Utilitaires
 *  @{
 */

/**
 * Effectue a^b
 * @param a mantisse
 * @param b exposant
 * @return a^b
 */
int puissance(int a, unsigned int b)
{
  if (b > 0)
    return a * puissance (a, b-1);
  else
    return 1;      
}

/**
 * Retourne le digit désiré d'un nombre.
 * @param a le nombre
 * @param b le digit
 * @return (a / 10^b) % 10^b
 */
unsigned int digit(const int& a, const int& b)
{
  unsigned int retour(0);
  retour = a / puissance(10, b);
  if (retour > 9)
	  retour = retour % 10;
  return retour;
}

/**
 * Transforme un entier en une string
 * @param nbr un nombre entier
 * @return la string correspondant à nbr
 */
string write_int(const unsigned int& nbr)
{
  stringstream out;
  out << nbr;
  return out.str();
}

/**
 * Demande à l'utilisateur d'entrer un nombre entier et vérifie que
 * la saisie est valide. Si la saisie est erronée, l'utilisateur est
 * demandé d'entrer un nombre valide.
 * @param question demande quoi entrer
 * @return le nombre entré
 */
unsigned int demander_unsigned(const string& question)
{
  bool encore(false);
  unsigned int retour;
  string poubelle;

  do
    {
      blank() << question;
      cin >> retour;
      Debug::Toggle(false);

      encore = cin.fail();

      if (encore)
	{
	  warning() << endreq << "Entrer un nombre entier!" << endreq;
	  cin.clear();
	  getline(cin, poubelle);
	}
    }
  while(encore);

  Debug::Toggle(false);

  return retour;
}

/**
 * Demande à l'utilisateur d'entrer un nombre entier et vérifie que
 * la saisie est valide. Si la saisie est erronée, l'utilisateur est
 * demandé d'entrer un nombre valide. Ce nombre est en plus borné.
 * @param question demande quoi entrer
 * @param min la borne inférieure
 * @param max la borne supérieure
 * @return le nombre entré
 */
unsigned int demander_unsigned_borne(const string& question,
				     const unsigned int& min,
				     const unsigned int& max)
{
  unsigned int nbr;
  bool encore(false);

  do
    {
      nbr = demander_unsigned(question);

      encore = (nbr < min || nbr > max);

      if (encore)
	warning() << "Entrer un nombre entier entre " << min
		   << " et " << max << " !" << endreq;
    }
  while(encore);

  Debug::Toggle(false);

  return nbr;
}

/**
 * Affiche les accents des sphères et écoles de magie.
 * Exemple : "Alt\\'eration" -> Altération.
 * @param brut la string originale.
 * @return la string contenant des accents lisibles.
 */
string affiche_accent(const string& brut)
{
  string retour(brut);
  unsigned int loc(retour.find("\\'", 0));
  while (loc != npos)
    {
      lfunc() << retour << ' ' << loc << " -> " <<endreq;
      retour.replace(loc, 3, "é");
      loc = retour.find("\\'", 0);
      lfunc() << retour << endreq;
    }
  loc = retour.find("\\`", 0);
  while (loc != npos)
    {
      lfunc() << retour << ' ' << loc << endreq;
      retour.replace(loc, 3, "à");
      loc = retour.find("\\`", 0);
    }
  loc = retour.find("\\^", 0);
  while (loc != npos)
    {
      lfunc() << retour << ' ' << loc << endreq;
      retour.replace(loc, 3, "ô");
      loc = retour.find("\\^", 0);
    }
  return retour;
}

/**
 * Affiche les sorts d'une école pour un niveau donné.
 * @param ecole numéro de l'école de magie
 * @param niveau niveau des sorts
 * @return le numéro du dernier sort
 */
unsigned int affiche_sorts(const unsigned int& ecole,
			   const unsigned int& niveau)
{
  const Sorts_ecoles *ptr;
  unsigned int taille, i;
  bool ok(true);

  switch(ecole)
    {
    case 0:
      ptr = &Abjuration[0];
      taille = 47;
      break;
    case 1:
      ptr = &Alteration[0];
      taille = 146;
      break;
    case 2:
      ptr = &Conjuration[0];
      taille = 60;
      break;
    case 3:
      ptr = &Divination[0];
      taille = 32;
      break;
    case 4:
      ptr = &Enchantement[0];
      taille = 58;
      break;
    case 5:
      ptr = &Illusion[0];
      taille = 56;
      break;
    case 6:
      ptr = &Invocation[0];
      taille = 73;
      break;
    case 7:
      ptr = &Necromancie[0];
      taille = 57;
      break;
    case 8:
      ptr = &Alchimie[0];
      taille = 38;
      break;
    case 9:
      ptr = &Artifices[0];
      taille = 22;
      break;
    case 10:
      ptr = &Dimension[0];
      taille = 22;
      break;
    case 11:
      ptr = &ElementAir[0];
      taille = 23;
      break;
    case 12:
      ptr = &ElementEau[0];
      taille = 23;
      break;
    case 13:
      ptr = &ElementFeu[0];
      taille = 23;
      break;
    case 14:
      ptr = &ElementTerre[0];
      taille = 27;
      break;
    case 15:
      ptr = &Eurythmique[0];
      taille = 43;
      break;
    case 16:
      ptr = &Forces[0];
      taille = 21;
      break;
    case 17:
      ptr = &Geometrie[0];
      taille = 41;
      break;
    case 18:
      ptr = &Mentalisme[0];
      taille = 24;
      break;
    case 19:
      ptr = &Ombres[0];
      taille = 36;
      break;
    case 20:
      ptr = &Universelle[0];
      taille = 17;
      break;
    default:
      warning() << "Erreur dans l'école de magie" << endreq;
      ok = false;
    }

  if (ok)
    {
      for (i = 0; i < taille; i++, ptr++)
	{
	  if (ptr -> niveau != niveau)
	    break;
	  blank() << setw(2) << ptr -> numero + 1 << ' '
		  << ptr -> Nom_sort << endreq;
	}

      return i;
    }
  return 0;
}

/**
 * Vérifie si une valeur est déjà dans un vecteur. Sert lors du choix d'une
 * compétence, d'un sort, ...
 *
 * Si valeur est déjà présent dans le vecteur adressé par ptr, le résultat
 * sera true, et on saura qu'il ne faut pas ajoutter la compétence, sort,...
 * @param ptr pointeur sur le vecteur à vérifier
 * @param valeur valeur à ajoutter
 * @return résultat du test
 */
bool safe_push_back(vector<unsigned int> *ptr, const unsigned int& valeur)
{
  vector<unsigned int>::iterator loc, Begin(ptr -> begin()), End(ptr -> end());
  loc = find(Begin, End, valeur);

  debug() << "Position : " << (int) (loc - Begin)
	  << " sur une taille de " << ptr -> size() << endreq;

  if (Begin == End)
      return false;

  return !(loc == End);
}

/**
 * Vérifie si une valeur est déjà dans un vecteur. Sert lors du choix d'une
 * compétence, sans tenir compte des pourcentages associés à certaines
 * compétences.
 *
 * Si valeur est présent dans le vecteur adressé par ptr mais pas dans
 * sauter, alors le résultat sera true. Par contre, si valeur est présent
 * à la fois dans ptr et sauter, alors on n'en tient pas compte et le résultat
 * est false.
 * @param ptr pointeur sur le vecteur à vérifier
 * @param valeur valeur à ajoutter
 * @param sauter vecteur des emplacements à ne pas vérifier
 * @return résultat du test
 */
bool safe_push_back(vector<unsigned int> *ptr, const unsigned int& valeur,
		    vector<unsigned int>& sauter)
{
  vector<unsigned int>::iterator loc, Begin(ptr -> begin()), End(ptr -> end()),
    sBegin(sauter.begin()), sEnd(sauter.end()), sIt, fBegin;
  bool retour(true);
  fBegin = Begin;

  if (Begin == End)
    return false;
  
  do
    {
      loc = find(fBegin, End, valeur);
      if (loc == End)
	{
	  retour = false;
	  break;
	}
      debug() << "A chercher : " << (unsigned int)(loc - Begin) << endreq;
      sIt = find(sBegin, sEnd, (unsigned int)(loc - Begin));
      if (sIt != sEnd)
	{
	  retour = false;
	  fBegin = ++loc;
	}
      else
	{
	  retour = true;
	  break;
	}
    }
  while(loc != End);
  
   debug() << "Position : " << (int) (loc - Begin)
 	  << " sur une taille de " << ptr -> size() << endreq;
  
  return retour;
}

/**
 * Retranscrit les arguments du programme principal pour gérer les noms
 * composés.
 * @param tab arguments originaux.
 * @param nbr dimension du tableau.
 * @param retour nouveaux arguments
 */
void gerer_espaces_blancs(char* tab[], int& nbr,
			  vector<string>& retour)
{
  retour.clear();
  for (unsigned int i(0); i < static_cast<unsigned int>(nbr); i++)
    {
      retour.push_back(tab[i]);
      debug() << retour.back() << endreq;
    }

  debug() << endreq;

  vector<string>::iterator it, Begin(++retour.begin()), End(retour.end());

  for (it = ++Begin; it != retour.end(); it++)
    {
      if ((*it)[0] < 48 || (*it)[0] > 57)
	{
	  *(it - 1) += "\\ " + *it;
	  retour.erase(it);
	  debug() << "Fusion en " << *(it -1) << endreq;
	  it--;
	}
    }

  debug() << endreq;

  unsigned int loc(retour[1].find("\'"));
  if (loc != npos)
    retour[1].replace(loc, 1, "\\'");

  nbr = retour.size();
}

/** @} */
